package com.wmx.spring;

import org.junit.Test;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;

/**
 * springframework 反射工具类 ReflectionUtils
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2022/1/14 17:08
 */
public class ReflectionUtilsTest {

    /**
     * Method findMethod(Class<?> clazz, String name, @Nullable Class<?>... paramTypes)
     * * 尝试在提供的类上找到具有提供的名称和参数类型的{@link Method}。搜索 Object 之前的所有超类
     * * 返回方法对象，如果未找到，则返回 null
     * * 如果目标方法的参数是泛型，比如 add(E e)，则可以使用 Object 指定
     * Method findMethod(Class<?> clazz, String name)：尝试在提供的类上找到具有提供的名称且没有参数的{@link Method}。搜索 Object 之前的所有超类
     *
     * @throws Exception
     */
    @Test
    public void testFindMethod() throws Exception {
        Class listClass = Class.forName("java.util.ArrayList");
        Object arrayList = listClass.newInstance();

        //对于方法的参数是泛型的，可以直接使用 Object 类型指定
        Method add = ReflectionUtils.findMethod(listClass, "add", Object.class);
        //[]
        System.out.println(arrayList);
        ReflectionUtils.invokeMethod(add, arrayList, 200);
        //[200]
        System.out.println(arrayList);
        ReflectionUtils.invokeMethod(add, arrayList, "蚩尤后裔");
        //[200, 蚩尤后裔]
        System.out.println(arrayList);
    }

    /**
     * Method[] getAllDeclaredMethods(Class<?> leafClass): 获得类中所有方法，包括继承而来的，包括私有的。如果子类父类中都有，则返回多个。
     * Method[] getUniqueDeclaredMethods(Class<?> leafClass)
     * * 获取自己和所有超类上声明的唯一方法集,不会重复返回。
     * * 首先包含子类方法，当遍历超类层次结构时，任何发现的具有与已包含的方法匹配的签名的方法都会被过滤掉。
     */
    @Test
    public void testGetAllDeclaredMethods() throws Exception {
        Class listClass = Class.forName("java.util.ArrayList");
        Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(listClass);
        Method[] uniqueDeclaredMethods = ReflectionUtils.getUniqueDeclaredMethods(listClass);
        int count = 1;
        for (Method method : uniqueDeclaredMethods) {
            //104：public java.lang.String java.lang.Object.toString()
            System.out.println((count++) + "：" + method);
        }
    }

    /**
     * Constructor<T> accessibleConstructor(Class<T> clazz, Class<?>... parameterTypes) 获取给定类和参数的可访问构造函数。
     * * 如果不存在此类构造函数，则引发NoSuchMethodException
     * Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args)
     * * 使用提供的参数对提供的目标对象调用指定的{@link Method}。调用静态方法时，目标对象可以是 null。
     * Object invokeMethod(Method method, @Nullable Object target)
     * * 对提供的目标对象调用指定的{@link Method}，不带任何参数。调用静态方法时，目标对象可以是 null。
     * void makeAccessible(Method method)  取消 Java 权限检查。以便后续执行该私有方法
     * void makeAccessible(Constructor<?> ctor) 取消 Java 权限检查。以便后续执行私有构造方法
     *
     * @throws Exception
     */
    @Test
    public void testAccessibleConstructor() throws Exception {
        Class listClass = Class.forName("java.util.ArrayList");
        Constructor constructor = ReflectionUtils.accessibleConstructor(listClass, int.class);

        //public java.util.ArrayList(int)
        System.out.println(constructor);

        Object instance = constructor.newInstance(8);

        Method addAll = ReflectionUtils.findMethod(listClass, "addAll", Collection.class);
        //public boolean java.util.ArrayList.addAll(java.util.Collection)
        System.out.println(addAll);

        ReflectionUtils.makeAccessible(addAll);
        ReflectionUtils.invokeMethod(addAll, instance, Arrays.asList(1, 2, 3));

        //[1, 2, 3]
        System.out.println(instance);
    }

    /**
     * boolean isEqualsMethod(Method method):是否是 equals() 方法
     * boolean isHashCodeMethod(Method method) 是否是 hashCode() 方法
     * boolean isToStringMethod(Method method) 是否是 toString() 方法
     * boolean isObjectMethod(Method method) 是否是从 Object 类继承而来的方法
     * boolean declaresException(Method method, Class<?> exceptionType) 检查一个方法是否声明抛出指定异常
     */
    @Test
    public void testIsObjectMethod() throws Exception {
        Class listClass = Class.forName("java.util.ArrayList");
        Method clone = ReflectionUtils.findMethod(listClass, "clone");

        //public java.lang.Object java.util.ArrayList.clone()
        System.out.println(clone);
        //true
        System.out.println(ReflectionUtils.isObjectMethod(clone));
    }

    /**
     * Field findField(Class<?> clazz, String name)
     * * 尝试在提供的 clazz 寻找具有指定属性名称(name)的{@link Field}。搜索{@link Object}之前的所有超类. 如果找不到，则返回 null
     * Field findField(Class<?> clazz, @Nullable String name, @Nullable Class<?> type)
     * * 尝试在提供的 clazz 寻找具有指定属性名称(name)和类型的{@link Field}。搜索{@link Object}之前的所有超类. 如果找不到，则返回 null
     * * name 字段的名称（如果指定了type，则可能为 null）
     * * type 字段的类型（如果指定了name，则可能为 null）
     * <p>
     * boolean isPublicStaticFinal(Field field) 确定给定字段是否为 “public static final” 常量。
     * void makeAccessible(Field field) 取消 Java 权限检查。以便后续执行该私有属性
     * void setField(Field field, @Nullable Object target, @Nullable Object value): 设置 target 对象的 field 属性值，值为 value
     * Object getField(Field field, @Nullable Object target) 获取 target 对象的 field 属性值
     * void shallowCopyFieldState(Object src, Object dest): 同类对象属性对等赋值
     * <p>
     * void doWithFields(Class<?> clazz, ReflectionUtils.FieldCallback fc) 对类的每个属性执行 callback
     * void doWithFields(Class<?> clazz, ReflectionUtils.FieldCallback fc,ReflectionUtils.FieldFilter ff) 同上，多了个属性过滤功能。
     * void doWithLocalFields(Class<?> clazz, ReflectionUtils.FieldCallback fc) 同上，但不包括继承而来的属性
     * <p>
     * void doWithMethods(Class<?> clazz, MethodCallback mc)
     * * 对给定类和超类的所有匹配方法执行给定的回调操作。
     * * 发生在子类和超类上的同一命名方法将出现两次，除非被{@link ReflectionUtils.MethodFilter}排除。
     * void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf)
     * void doWithLocalMethods(Class<?> clazz, MethodCallback mc) 对给定类的所有匹配方法执行给定的回调操作,不包括继承的。
     *
     * @throws Exception
     */
    @Test
    public void testFindField() throws Exception {
        Class bookClass = Class.forName("com.wmx.spring.Book");
        Object instance = bookClass.newInstance();
        Field titleField = ReflectionUtils.findField(bookClass, "title");

        //private java.lang.String com.wmx.spring.Book.title
        System.out.println(titleField);

        ReflectionUtils.makeAccessible(titleField);
        ReflectionUtils.setField(titleField, instance, "三国演义");

        Object fieldValue = ReflectionUtils.getField(titleField, instance);
        //三国演义
        System.out.println(fieldValue);

        ReflectionUtils.doWithFields(bookClass, field -> {
            //private java.lang.String com.wmx.spring.Book.bid
            System.out.println(field);
            ReflectionUtils.makeAccessible(field);
            switch (field.getName()) {
                case "bid":
                    ReflectionUtils.setField(field, instance, "p1001");
                    break;
                case "price":
                    ReflectionUtils.setField(field, instance, 89.56F);
                    break;
                case "publishDate":
                    ReflectionUtils.setField(field, instance, new Date());
                    break;
                case "isShow":
                    ReflectionUtils.setField(field, instance, true);
                    break;
                case "stock":
                    ReflectionUtils.setField(field, instance, 800);
                    break;
            }
        });

        //Book{bid='p1001', title='三国演义', price=89.56, publishDate=Fri Jan 14 19:59:59 CST 2022, isShow=true, stock=800}
        System.out.println(instance);
    }


}


